﻿using UnityEngine;
using UnityEditor;
using System.Collections;
using System.Collections.Generic;

public class TileMapWindow : EditorWindow {

	const string resourcePath = "Tiles";
	ToolState currentTool;
	GameObject plane;
	TileMap target;
	GUIStyle labelStyle;
	
	GameObject[] brushes;
	GameObject brush_Current;
	GUIContent[] brush_Previews;
	Vector2 brush_ScrollPos;
	Vector3 brush_StartDragging;
	int brush_Index;
	int brush_Orientation = 1;
	string[] brush_OrientationSymbol = new string[] { "", "^", "", "<", "", ">", "", "v", "" };
	int[] brush_OrientationProgression = new int[] { 1, 5, 7, 3 };

	bool isDragged;
	bool isClicked;

	enum ToolState
	{
		Brush  = 0,
		Eraser = 1,
		Select = 2
	}

	[MenuItem("Window/TileMap Editor")]
	static void ShowEditor()
	{
		var window = GetWindow<TileMapWindow>("TileMap Editor");
		window.Show();
	}

	void ReloadPlane()
	{
		GameObject _plane = null;

		while((_plane = GameObject.Find("TWM_Plane")) != null)
		{
			DestroyImmediate(_plane);
		}

		if(plane != null)
		{
			DestroyImmediate(plane);
		}

		plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
		plane.transform.position = Vector3.zero;
		plane.transform.rotation = Quaternion.identity;
		plane.transform.localScale = Vector3.one * 100f;

		plane.GetComponent<MeshRenderer>().enabled = false;
		plane.name = "TMW_Plane";
	}

	void OnEnable()
	{
		ReloadPlane();
		ReloadBrushes();
		currentTool = ToolState.Brush;
		SceneView.onSceneGUIDelegate += OnSceneGUI;
	}
	void OnDisable()
	{
		SceneView.onSceneGUIDelegate -= OnSceneGUI;
		brush_Previews = null;
	}
	void OnFocus()
	{
		SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
		SceneView.onSceneGUIDelegate += this.OnSceneGUI;
		ReloadPlane();
		ReloadBrushes();
	}
	void OnDestroy()
	{
		SceneView.onSceneGUIDelegate -= this.OnSceneGUI;
	}
	void OnInspectorUpdate()
	{
		var obj = (GameObject)Selection.activeGameObject;
		if (obj != null)
			target = obj.GetComponent<TileMap>();
		else
			target = null;

		Repaint();
	}

	void OnGUI()
	{
		GUILayout.Space(10f);
		if (target != null)
		{
				//Button radios
				DoToolButtonsGUI();
				GUILayout.Space(10f);
				switch (currentTool)
				{
					case ToolState.Brush:
						DoBrushGUI();
					break;

					case ToolState.Eraser:
						DoEraserGUI();
					break;

					case ToolState.Select:
						DoSelectGUI();
					break;

					default:
					break;
				}

		}
		else
		{
			GUIStyle style = new GUIStyle();
			style.alignment = TextAnchor.MiddleCenter;
			style.stretchHeight = true;
			style.stretchWidth = true;
			style.wordWrap = true;
			EditorGUILayout.TextField("Please Select a TileMap", style, GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true));
		}
	}
	void OnSceneGUI(SceneView view)
	{
		if (target != null)
		{
			DoToolButtons();
			switch (currentTool)
			{
				case ToolState.Brush:
					DoBrush();
					break;

				case ToolState.Eraser:
					DoEraser();
					break;

				case ToolState.Select:
					DoSelect();
					break;

				default:
					break;
			}

		}
	}

	void DoToolButtons()
	{
		if (brush_Current != null && currentTool != ToolState.Brush)
			brush_Current.SetActive(false);
	}
	void DoToolButtonsGUI()
	{
		GUILayout.BeginHorizontal();
			GUILayout.Label("Mode");
		GUILayout.EndHorizontal();
		GUILayout.Space(5f);

		GUILayout.BeginHorizontal();
			var style = new GUIStyle(GUI.skin.button);
			style.padding = new RectOffset(5, 5, 10, 10);		
			currentTool = (ToolState)GUILayout.SelectionGrid((int)currentTool, new string[] { "Brush", "Eraser", "Select" }, 3, style);
		GUILayout.EndHorizontal();
	}

	void ReloadBrushes()
	{
		var obj = Resources.LoadAll<GameObject>(resourcePath);
		var previewTex = new List<GUIContent>();

		foreach (var item in obj)
		{
			var t = new GUIContent(item.name,AssetPreview.GetAssetPreview(item));
			previewTex.Add(t);
		}

		brushes = obj;
		brush_Previews = previewTex.ToArray();
	}
	void ReloadCurrentBrush()
	{
		if (brush_Current != null)
			DestroyImmediate(brush_Current);

		brush_Current = Instantiate(brushes[brush_Index], Vector3.zero, Quaternion.identity) as GameObject;
		brush_Current.name = brushes[brush_Index].GetHashCode().ToString();
		brush_Current.layer = 2;
	}
	void DoBrush()
	{
		if(brush_Current == null || brush_Current.name != brushes[brush_Index].GetHashCode().ToString())
			ReloadCurrentBrush();

		var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
		var tilePos = new Vector3();
		var visible = true;
		RaycastHit hit;

		if (Physics.Raycast(ray, out hit))
		{
			//Not hitting base plane
			if (hit.collider.gameObject.name != "TMW_Plane")
			{
				if (hit.normal.y > 0)
				{
					tilePos = hit.collider.gameObject.transform.position + new Vector3(0, 1f, 0);
				}
				//bot of box
				else if (hit.normal.y < 0)
				{
					tilePos = hit.collider.gameObject.transform.position + new Vector3(0, -1f, 0);
				}
				else
				{
					tilePos = hit.collider.gameObject.transform.position + hit.normal;
				}
			}
			else if (hit.collider.gameObject.name == "TMW_Plane")
			{
				tilePos.x = Mathf.FloorToInt(hit.point.x) + 0.5f;
				tilePos.y = 0.5f;
				tilePos.z = Mathf.FloorToInt(hit.point.z) + 0.5f;
			}
			else
				visible = false;

		}

		if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
		{
			isClicked = true;
			brush_StartDragging = tilePos;
			Event.current.Use();
		}

		if (Event.current.type == EventType.MouseDrag && Event.current.button == 0)
		{
			isDragged = true;
			isClicked = false;
			
			if (target.GetTileCountAt((int)(tilePos.x - 0.5f), (int)(tilePos.z - 0.5f)) == (int)(brush_StartDragging.y - 0.5f))
			{
				brushes[brush_Index].transform.rotation = brush_Current.transform.rotation;
				target.AddTile((int)(tilePos.x - 0.5f), (int)(tilePos.z - 0.5f), brushes[brush_Index]);
				Event.current.Use();
			}
		}

		if (isDragged)
		{
			tilePos.y = brush_StartDragging.y;
		}

		if (Event.current.type == EventType.MouseUp && Event.current.button == 0)
		{
			if (!isDragged && isClicked)
			{
				brushes[brush_Index].transform.rotation = brush_Current.transform.rotation;
				target.AddTile((int)(tilePos.x - 0.5f), (int)(tilePos.z - 0.5f), brushes[brush_Index]);
			}

			isDragged = false;
			isClicked = true;
			Event.current.Use();
		}

		if (Event.current.type == EventType.KeyDown)
		{
			var evt_use = true;
			//hardcode ftw!!!!!
			switch (Event.current.keyCode)
			{
				case KeyCode.LeftArrow:
					{
						if (brush_Orientation == 1)
							brush_Orientation = 3;
						else if (brush_Orientation == 3)
							brush_Orientation = 7;
						else if (brush_Orientation == 7)
							brush_Orientation = 5;
						else if (brush_Orientation == 5)
							brush_Orientation = 1;
					}
					break;
				case KeyCode.RightArrow:
					{
						if (brush_Orientation == 1)
							brush_Orientation = 5;
						else if (brush_Orientation == 5)
							brush_Orientation = 7;
						else if (brush_Orientation == 7)
							brush_Orientation = 3;
						else if (brush_Orientation == 3)
							brush_Orientation = 1;
					}
					break;
				default:
					evt_use = false;
					break;
			}

			if (evt_use)
				Event.current.Use();
		}

		//Orientation
		var rot = 0.0f;
		if (brush_Orientation == 3)
			rot = 270.0f;
		if (brush_Orientation == 5)
			rot = 90.0f;
		if (brush_Orientation == 7)
			rot = 180.0f;

		//Draw appropriately
		brush_Current.SetActive(visible);
		brush_Current.transform.position = tilePos;
		brush_Current.transform.localEulerAngles = new Vector3( brush_Current.transform.localEulerAngles.x,
																rot,
																brush_Current.transform.localEulerAngles.z);

		//Draw debug orientation
		/*
		Handles.color = new Color(0, 1, 0, 0.5f);
		Handles.ArrowCap(0, brush_Current.transform.position, brush_Current.transform.rotation, 1.0f);
		HandleUtility.Repaint();*/
	}
	void DoBrushGUI()
	{
		GUILayout.BeginHorizontal();
		GUILayout.Label("Brush Set");
		if (GUILayout.Button("Reload Brushes",GUILayout.ExpandWidth(false)))
			ReloadBrushes();
		GUILayout.EndHorizontal();
		GUILayout.Space(5f);

		GUILayout.BeginVertical( GUIStyle.none, GUILayout.MinHeight(64),GUILayout.MaxHeight(64*3),GUILayout.ExpandHeight(true));
		brush_ScrollPos = GUILayout.BeginScrollView(brush_ScrollPos);
			var bstyle = new GUIStyle(GUI.skin.button);
			bstyle.imagePosition = ImagePosition.ImageAbove;
			bstyle.fixedHeight = 64;
			bstyle.fixedWidth = 64;
			brush_Index = GUILayout.SelectionGrid(brush_Index, brush_Previews, Mathf.FloorToInt(position.width/64), bstyle, GUILayout.ExpandHeight(false), GUILayout.ExpandWidth(false));
		GUILayout.EndScrollView();
		GUILayout.EndVertical();

		GUILayout.Label("Orientation");
		GUILayout.Space(10f);
			var ostyle = new GUIStyle(GUI.skin.button);
			ostyle.fixedHeight = 32;
			ostyle.fixedWidth = 32;
			var orientation = GUILayout.SelectionGrid(brush_Orientation, brush_OrientationSymbol,3,ostyle);
				if(orientation % 2 == 0)
					orientation = brush_Orientation;
				else
					brush_Orientation = orientation;
		GUILayout.FlexibleSpace();
	}

	void DoEraser()
	{
		var ray = HandleUtility.GUIPointToWorldRay(Event.current.mousePosition);
		var tilePos = new Vector3();
		RaycastHit hit;

		if (Physics.Raycast(ray, out hit))
		{
			//Not hitting base plane
			if (hit.collider.gameObject.name != "TMW_Plane")
			{
				tilePos = hit.collider.gameObject.transform.position;
			}
			else if (hit.collider.gameObject.name == "TMW_Plane")
			{
				tilePos.x = Mathf.FloorToInt(hit.point.x) + 0.5f;
				tilePos.y = 0.5f;
				tilePos.z = Mathf.FloorToInt(hit.point.z) + 0.5f;
			}
			

			if (Event.current.type == EventType.MouseDown && Event.current.button == 0)
			{
				isClicked = true;

				Event.current.Use();
			}

			if (Event.current.type == EventType.MouseDrag && Event.current.button == 0)
			{
				isDragged = true;
				isClicked = false;
				Event.current.Use();
				
				target.RemoveTile((int)(tilePos.x - 0.5f), (int)(tilePos.z - 0.5f), (int)(tilePos.y - 0.5f));
			}

			if (Event.current.type == EventType.MouseUp && Event.current.button == 0)
			{
				if (!isDragged && isClicked)
					target.RemoveTile((int)(tilePos.x - 0.5f), (int)(tilePos.z - 0.5f), (int)(tilePos.y - 0.5f));

				isDragged = false;
				isClicked = true;

				Event.current.Use();
			}

			DoEraserCube(tilePos, 1.0f);
		}
	}
	void DoEraserCube(Vector3 pos, float size)
	{
		Handles.color = Color.green;
		Handles.DrawLine(pos + new Vector3(size / 2, -size / 2, size / 2), pos + new Vector3(size / 2, -size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(size / 2, -size / 2, -size / 2), pos + new Vector3(-size / 2, -size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, -size / 2, -size / 2), pos + new Vector3(-size / 2, -size / 2, size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, -size / 2, size / 2), pos + new Vector3(size / 2, -size / 2, size / 2));

		Handles.DrawLine(pos + new Vector3(size / 2, size / 2, size / 2), pos + new Vector3(size / 2, size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(size / 2, size / 2, -size / 2), pos + new Vector3(-size / 2, size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, size / 2, -size / 2), pos + new Vector3(-size / 2, size / 2, size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, size / 2, size / 2), pos + new Vector3(size / 2, size / 2, size / 2));

		Handles.DrawLine(pos + new Vector3(size / 2, -size / 2, size / 2), pos + new Vector3(size / 2, size / 2, size / 2));
		Handles.DrawLine(pos + new Vector3(size / 2, -size / 2, -size / 2), pos + new Vector3(size / 2, size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, -size / 2, -size / 2), pos + new Vector3(-size / 2, size / 2, -size / 2));
		Handles.DrawLine(pos + new Vector3(-size / 2, -size / 2, size / 2), pos + new Vector3(-size / 2, size / 2, size / 2));
		HandleUtility.Repaint();
	}
	void DoEraserGUI()
	{

	}

	void DoSelect()
	{

	}
	void DoSelectGUI()
	{

	}
}
